/* lfsmkdir.c  -  lfsmkdir */

#include <xinu.h>
#include <string.h>

/*------------------------------------------------------------------------
 * lfsmkdir  --  create a directory-type file on disk
 *------------------------------------------------------------------------
 */
status	lfsmkdir (
	struct	dentry	*devptr,	/* entry in device switch table		*/
	char	*name				/* name of dir-type file to open	*/
	)
{
	struct	lfdir	*dirptr;	/* ptr to in-memory sector 0	*/
	char	*from, *to;			/* ptrs used during copy		*/
	char	*nam, *cmp;			/* ptrs used during comparison	*/
	char	*finalfname;		/* final file name to open		*/
	char	*dname;
	//char	*fname;				/* dir name holding file and file name	*/
	//char	*dname;
	char	*pname;				/* file name to open during recursion	*/
	int32	i, j, k;				/* general loop index			*/
	did32	lfnext;				/* minor number of an unused	*/
								/*    file pseudo-device		*/
	struct	ldentry	*ldptr;		/* ptr to dir entry for opening file	*/
	struct	ldentry *ldpaptr;	/* ptr to dir entry used in search		*/
	struct	ldentry *ldtemptr;	/* ptr to dir entry used in search		*/
	struct	lflcblk	*lfptr;		/* ptr to open file table entry	*/
	bool8	found;				/* was the name found?			*/
	int32	retval;				/* value returned from function	*/
	char	path[LF_TREE_HEIGHT][LF_NAME_LEN];	/* store the paths \
												in the name	*/
	uint32	pathnum;			/* record # dirs along the path	*/
	int32	dirsize;			/* dir size used during search	*/
	int32	pos, pos1, pos2;				/* where to add new dir entry	*/
	did32 	fd;					/* file opened during recursion	*/
	//struct	lfiblk	ibptr;		/* ptr to in-memory index block	*/
	//char	*ldbuff;			/* buff holding dir entry during search	*/

	/* parse the file name into directory along the path */
	from = name;
	j = k = 0;
	if (from[0] != '/') {
		kprintf("File name should start with '/'\r\n");
		return SYSERR;
	}
	for(i=1; from[i] != NULLCH; i++) {
		if(from[i] != '/') {
			path[j][k++] = from[i];
		}
		else {	
			path[j][k] = NULLCH;
			j ++;
			k = 0;
		}
	}
	
	path[j][k] = NULLCH;
	pathnum = j;
	finalfname = path[pathnum];	/* actual file name to open	*/
	
	for (i=0; i<=pathnum; i++) {
		kprintf("path[%d] = %s\r\n", i, path[i]);
	}
	kprintf("finalfname = %s\r\n", finalfname);
	
	/* Check length of name file (leaving space for NULLCH */
	from = finalfname;
	for (i=0; i< LF_NAME_LEN; i++) {
		if (*from++ == NULLCH) {
			break;
		}
	}
	if (i >= LF_NAME_LEN) {		/* name is too long */
		kprintf("Name is too long\r\n");
		return SYSERR;
	}
	
	/* If named file is already open, return SYSERR */
	lfnext = SYSERR;
	for (i=0; i<Nlfl; i++) {	/* search file pseudo-devices	*/
		lfptr = &lfltab[i];
		if (lfptr->lfstate == LF_FREE) {
			if (lfnext == SYSERR) {
				lfnext = i; /* record index */
			}
			continue;
		}
		/* Compare requested name to name of open file */
		nam = finalfname;
		cmp = lfptr->lfname;
		while(*nam != NULLCH) {
			if (*nam != *cmp) {
				break;
			}
			nam++;
			cmp++;
		}
		/* See if comparison succeeded */
		if ( (*nam==NULLCH) && (*cmp == NULLCH) ) {
			kprintf("file is already open\r\n");
			return SYSERR;
		}
	}
	if (lfnext == SYSERR) {	/* no slave file devices are available	*/
		kprintf("No slave file devices are available\r\n");
		return SYSERR;
	}

	/* Obtain copy of Sector 0 if not already present in memory	*/
	dirptr = &Lf_data.lf_dir;
	wait(Lf_data.lf_mutex);
	if (! Lf_data.lf_dirpresent) {
	    retval = read(Lf_data.lf_dskdev,(char *)dirptr,LF_AREA_DIR);
	    if (retval == SYSERR ) {
		signal(Lf_data.lf_mutex);
		kprintf("Cannot not read Sector 0 into memory\r\n");
		return SYSERR;
	    }
	    kprintf("Read sector 0 done!\r\n");
	    kprintf("dirptr->lfd_dfree = %d\r\n", dirptr->lfd_dfree);
	    kprintf("dirptr->lfd_ifree = %d\r\n", dirptr->lfd_ifree);
	    kprintf("(dirptr->root_entry).ld_size = %d\r\n", (dirptr->root_entry).ld_size);
	    kprintf("(dirptr->root_entry).ld_ilist = %d\r\n", (dirptr->root_entry).ld_ilist);
	    kprintf("(dirptr->root_entry).ld_name = %s\r\n", (dirptr->root_entry).ld_name);
	    //Lf_data.lf_dirpresent = TRUE;
	}

	/* If open file in the root directory, e.g file name - /a		*/
	
	if (pathnum == 0) {
		kprintf("\r\nOpen file in the root directory\r\n");
		/* Initialize the local file pseudo-device */
		lfptr = &lfltab[lfnext];
		lfptr->lfdirptr = &(dirptr->root_entry);/* point to root directory entry*/
		lfptr->lfstate = LF_USED;
		/* File starts at position 0 */
		lfptr->lfpos     = 0;
		lfptr->lfname[0] = '/';
		lfptr->lfname[1] = NULLCH;
		/* Neither index block nor data block are initially valid	*/
		lfptr->lfinum    = LF_INULL;
		lfptr->lfdnum    = LF_DNULL;
		/* Initialize byte pointer to address beyond the end of the	*/
		/*	buffer (i.e., invalid pointer triggers setup)		*/
		lfptr->lfbyte = &lfptr->lfdblock[LF_BLKSIZ];
		lfptr->lfibdirty = FALSE;
		lfptr->lfdbdirty = FALSE;
		kprintf("Initialize the local file pseudo-device done\r\n");
		
		/* Load the root directory into memory, "open" root file 	*/	
		/* Search root directory to see if file exists */
		signal(Lf_data.lf_mutex);
		kprintf("(lfptr->lfdirptr)->ld_size = %d\r\n", (lfptr->lfdirptr)->ld_size);
		if ((lfptr->lfdirptr)->ld_size > 0) {
				ldpaptr = &Lf_data.lf_path[0];
			}
		dirsize = read(lfptr->lfdev, (char *)ldpaptr, \
		(lfptr->lfdirptr)->ld_size);
		wait(Lf_data.lf_mutex);
		kprintf("Read done!\r\n");
		found = FALSE;
		for (i=0; i<dirsize/sizeof(struct ldentry); i++) {
			nam = finalfname;
			cmp = ldpaptr->ld_name;
			kprintf("ldpaptr->ld_name = %s\r\n", ldpaptr->ld_name);
			while(*nam != NULLCH) {
				if (*nam != *cmp) {
					break;
				}
				nam++;
				cmp++;
			}
			if ( (*nam==NULLCH) && (*cmp==NULLCH) ) { /* name found	*/
				found = TRUE;
				break;
			}
			ldpaptr++;
		}
		pos = i;
		kprintf("Load the root directory into memory done!\r\n");
		
		/* Case #1 - file is not in directory (i.e., does not exist)	*/
		if (! found) {
			kprintf("file is not in directory begins!\r\n");
			/* Take steps to create new file and add to root directory	*/
			/* Allocate next dir. entry & initialize to empty file	*/
			kprintf("Create new file add to root begins!\r\n");
			ldptr = &Lf_data.lf_newdentry;
			ldptr->ld_size = 0;
			ldptr->ld_type = LF_TYPE_DIR;
			from = finalfname;
			to = ldptr->ld_name;
			while ( (*to++ = *from++) != NULLCH ) {
				;
			}
			ldptr->ld_ilist = LF_INULL;
			kprintf("Create new file add to root half done!\r\n");
			/* temporarily give up exclusive access to Sector 0	*/
			signal(Lf_data.lf_mutex);
			seek(lfptr->lfdev, pos*sizeof(struct ldentry));
			//kprintf("pos*sizeof(struct ldentry) = %d\r\n", pos*sizeof(struct ldentry));
			write(lfptr->lfdev, (char *)ldptr, sizeof(struct ldentry));
			write(Lf_data.lf_dskdev,(char *)dirptr,LF_AREA_DIR);
			//kprintf("dirptr->root_entry.ld_size) = %d\r\n", (lfptr->lfdirptr)->ld_size);
			seek(lfptr->lfdev, 0);
			ldtemptr = &Lf_data.lf_path[0];
			dirsize = read(lfptr->lfdev, (char	*)ldtemptr, \
			(lfptr->lfdirptr)->ld_size);
			//kprintf("ldtemptr->ld_name = %s\r\n", ldtemptr->ld_name);
			ldtemptr ++;
			//kprintf("ldtemptr->ld_name = %s\r\n", ldtemptr->ld_name);
			/* obtain exclusive access to Sector 0	*/
			wait(Lf_data.lf_mutex);
			kprintf("file is not in directory done!\r\n");
		}
		/* Case #2 - file is in directory (i.e., already exists)	*/
		else {
			kprintf("Case #2 - file is in directory begins\r\n");
			if (ldpaptr->ld_type == LF_TYPE_DIR) {
				signal(Lf_data.lf_mutex);
				kprintf("Direcotry %s already exists\r\n", ldpaptr->ld_name);
				return SYSERR;
				}
		}
		kprintf("Update the local file pseudo-device begins\r\n");
		/* Update the local file pseudo-device */
		lfptr->lfstate = LF_USED;
		lfptr->lfdirptr = ldptr;	/* point to directory entry	*/
		/* File starts at position 0 */
		lfptr->lfpos     = 0;
		to = lfptr->lfname;
		from = finalfname;
		while ( (*to++ = *from++) != NULLCH ) {
			;
		}
		/* Neither index block nor data block are initially valid	*/
		lfptr->lfinum    = LF_INULL;
		lfptr->lfdnum    = LF_DNULL;
		/* Initialize byte pointer to address beyond the end of the	*/
		/*	buffer (i.e., invalid pointer triggers setup)		*/
		lfptr->lfbyte = &lfptr->lfdblock[LF_BLKSIZ];
		lfptr->lfibdirty = FALSE;
		lfptr->lfdbdirty = FALSE;
		signal(Lf_data.lf_mutex);
		kprintf("lfsOpen Done\r\n\r\n");
		return lfptr->lfdev;
	}
	
	/* If open file is not in root dir, e.g file name - /a/b/c/d...	*/
	else {
		kprintf("\r\nOpen file not in the root directory\r\n");
		/* record first two element along the path, e.g. a, b */
		dname = path[0];
		finalfname = path[1];
		/* load the directory file (/a) into memory		*/
		/* First, load the root directory into memory	*/
		/* Initialize the local file pseudo-device 		*/
		lfptr = &lfltab[lfnext];
		lfptr->lfdirptr = &(dirptr->root_entry);/* point to root directory entry*/
		lfptr->lfstate = LF_USED;
		/* File starts at position 0 */
		lfptr->lfpos     = 0;
		lfptr->lfname[0] = '/';
		lfptr->lfname[1] = NULLCH;
		/* Neither index block nor data block are initially valid	*/
		lfptr->lfinum    = LF_INULL;
		lfptr->lfdnum    = LF_DNULL;
		/* Initialize byte pointer to address beyond the end of the	*/
		/*	buffer (i.e., invalid pointer triggers setup)		*/
		lfptr->lfbyte = &lfptr->lfdblock[LF_BLKSIZ];
		lfptr->lfibdirty = FALSE;
		lfptr->lfdbdirty = FALSE;
		/* Load root directory into memory to see if directory /a exists */
		signal(Lf_data.lf_mutex);
		if ((lfptr->lfdirptr)->ld_size > 0) {
			ldpaptr = &Lf_data.lf_path[0];
		}
		dirsize = read(lfptr->lfdev, (char	*)ldpaptr, \
		(lfptr->lfdirptr)->ld_size);
		wait(Lf_data.lf_mutex);
		found = FALSE;
		for (i=0; i<dirsize/sizeof(struct ldentry); i++) {
			nam = dname;
			//kprintf("dname = %s\r\n", dname);
			cmp = ldpaptr->ld_name;
			while(*nam != NULLCH) {
				if (*nam != *cmp) {
					break;
				}
				nam++;
				cmp++;
			}
			if ( (*nam==NULLCH) && (*cmp==NULLCH) ) { /* name found	*/
				if (ldpaptr->ld_type == LF_TYPE_DIR) {
					found = TRUE;
					break;
				}
			}
			ldpaptr++;
		}
		pos1 = i;
		if (! found) {
			signal(Lf_data.lf_mutex);
			kprintf("No such directory %s exists\r\n", dname);
			return SYSERR;
		}
		/* directory /a exists, load it into memory		*/
		else {
			kprintf("directory /a exists, load it into memory\r\n");
			/* Update the local file pseudo-device */
			lfptr->lfstate = LF_USED;
			lfptr->lfdirptr = ldpaptr;	/* point to directory entry	*/
			/* File starts at position 0 */
			lfptr->lfpos     = 0;
			to = lfptr->lfname;
			from = finalfname;
			while ( (*to++ = *from++) != NULLCH ) {
				;
			}
			/* Neither index block nor data block are initially valid	*/
			lfptr->lfinum    = LF_INULL;
			lfptr->lfdnum    = LF_DNULL;
			/* Initialize byte pointer to address beyond the end of the	*/
			/*	buffer (i.e., invalid pointer triggers setup)		*/
			lfptr->lfbyte = &lfptr->lfdblock[LF_BLKSIZ];
			lfptr->lfibdirty = FALSE;
			lfptr->lfdbdirty = FALSE;
			/* Search /a directory to see if b exists */
			signal(Lf_data.lf_mutex);
			if ((lfptr->lfdirptr)->ld_size > 0) {
				ldpaptr = &Lf_data.lf_path[0];
			}
			dirsize = read(lfptr->lfdev, (char *)ldpaptr, \
			(lfptr->lfdirptr)->ld_size);
			wait(Lf_data.lf_mutex);
			found = FALSE;
			for (i=0; i<dirsize/sizeof(struct ldentry); i++) {
				nam = finalfname;
				kprintf("dname = %s\r\n", dname);
				cmp = ldpaptr->ld_name;
				kprintf("ldpaptr->ld_name = %s\r\n", ldpaptr->ld_name);
				while(*nam != NULLCH) {
					if (*nam != *cmp) {
						break;
					}
					nam++;
					cmp++;
				}
				if ( (*nam==NULLCH) && (*cmp==NULLCH) ) { /* name found	*/
					found = TRUE;
					break;
				}
				ldpaptr++;
			}
			pos2 = i;
			/* only one dir along the path, e.g. b is a file		*/
			if (pathnum == 1) {
				kprintf("only one dir along the path, e.g. b is a file\r\n");
				/* Case #1 - file b is not in directory a		*/
				if (! found) {
					kprintf("Case #1 - file b is not in directory a\r\n");
					/* Take steps to create new file and add to parent directory	*/
					/* Allocate next dir. entry & initialize to empty file	*/
					ldptr = &Lf_data.lf_newdentry;
					ldptr->ld_size = 0;
					ldptr->ld_type = LF_TYPE_DIR;
					from = finalfname;
					to = ldptr->ld_name;
					while ( (*to++ = *from++) != NULLCH ) {
						;
					}
					ldptr->ld_ilist = LF_INULL;
					signal(Lf_data.lf_mutex);
					seek(lfptr->lfdev, pos2*sizeof(struct ldentry));
					write(lfptr->lfdev, (char *)ldptr, sizeof(struct ldentry));
					seek(lfptr->lfdev, 0);
					kprintf("ldptr->ld_name = %s\r\n", ldptr->ld_name);
					kprintf("ldptr->ld_ilist = %d\r\n", ldptr->ld_ilist);
					kprintf("ldptr->ld_size = %d\r\n", ldptr->ld_size);
					ldtemptr = &Lf_data.lf_path[0]; 
					dirsize = read(lfptr->lfdev, (char	*)ldtemptr, \
					(lfptr->lfdirptr)->ld_size);
					kprintf("ldpaptr->ld_name = %s\r\n", ldpaptr->ld_name);
					kprintf("ldpaptr->ld_ilist = %d\r\n", ldpaptr->ld_ilist);
					kprintf("ldpaptr->ld_size = %d\r\n", ldpaptr->ld_size);
					/* write parent direcotry file in root entry again	*/
					lfptr->lfdirptr = &(dirptr->root_entry);/* point to root directory entry*/
					lfptr->lfpos	= pos1*sizeof(struct ldentry);
					lfptr->lfname[0] = '/';
					lfptr->lfname[1] = NULLCH;
					lfptr->lfinum    = LF_INULL;
					lfptr->lfdnum    = LF_DNULL;
					lfptr->lfbyte = &lfptr->lfdblock[LF_BLKSIZ];
					signal(Lf_data.lf_mutex);
					write(lfptr->lfdev, (char *)ldpaptr, \
					sizeof(struct ldentry));
					seek(lfptr->lfdev, 0);
					ldtemptr = &Lf_data.lf_path[0];
					dirsize = read(lfptr->lfdev, (char	*)ldtemptr, \
					(lfptr->lfdirptr)->ld_size);
					/* change root file in sector 0 again	*/
					write(Lf_data.lf_dskdev,(char *)dirptr,LF_AREA_DIR);
					wait(Lf_data.lf_mutex);
				}
				/* Case #2 - file b is in directory a		*/
				else {
					kprintf("Case #1 - file b is in directory a\r\n");
					if (ldpaptr->ld_type == LF_TYPE_DIR) {
						signal(Lf_data.lf_mutex);
						kprintf("Direcotry %s already exists\r\n", ldpaptr->ld_name);
						return SYSERR;
						}
				}
				/* Update the local file pseudo-device */
				lfptr->lfstate = LF_USED;
				lfptr->lfdirptr = ldptr;	/* point to directory entry	*/
				/* File starts at position 0 */
				lfptr->lfpos     = 0;
				to = lfptr->lfname;
				from = finalfname;
				while ( (*to++ = *from++) != NULLCH ) {
					;
				}
				/* Neither index block nor data block are initially valid	*/
				lfptr->lfinum    = LF_INULL;
				lfptr->lfdnum    = LF_DNULL;
				/* Initialize byte pointer to address beyond the end of the	*/
				/*	buffer (i.e., invalid pointer triggers setup)		*/
				lfptr->lfbyte = &lfptr->lfdblock[LF_BLKSIZ];
				lfptr->lfibdirty = FALSE;
				lfptr->lfdbdirty = FALSE;
				signal(Lf_data.lf_mutex);
				kprintf("lfsOpen Done\r\n\r\n");
				return lfptr->lfdev;
			}
			/* more than 1 dir along the path, e.g. b is a dir itself	*/
			else {
				kprintf("more than 1 dir along the path\r\n");
				/* Case #1 - dir b is not in directory a		*/
				if (! found) {
					kprintf("Case #1 - dir b is not in directory a\r\n");
					signal(Lf_data.lf_mutex);
					kprintf("No such directory %s exists\r\n", finalfname);
					return SYSERR;
					}
				/* Case #2 - dir b is in directory a		*/
				else {
					kprintf("Case #2 - dir b is in directory a\r\n");
					pname = strchr(name+1, '/');
					kprintf("pname = %s\r\n", pname);
					if (strchr(pname+1, '/') == NULL) {
						signal(Lf_data.lf_mutex);
						kprintf("DIR type file cannot be opened\r\n");
						return SYSERR;
					}
					else {
						fd = open(LFILESYS, pname, "rw");
					}
				}
			}
		}
	}

	return SYSERR;
}
